#include "udp-net-communication.h"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <vector>

UdpNetCommunication::UdpNetCommunication(const string &localIP, uint16_t loaclPort)
{
    socketfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socketfd < 0)
    {
        perror("FAil to create socket for udp");
        return;
    }

    // 绑定
    struct sockaddr_in sockaddr;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = inet_addr(localIP.c_str());
    sockaddr.sin_port = htons(loaclPort);
    int ret = bind(socketfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
    if (ret < 0)
    {
        perror("Fail to bind");
        return;
    }
}

/**
 * 发送数据包
 */
bool UdpNetCommunication::sendPacket(const vector<uint8_t> &packet)
{
    struct sockaddr_in peerAddr;
    peerAddr.sin_family = AF_INET;
    peerAddr.sin_addr.s_addr = inet_addr(peerIP.c_str());
    peerAddr.sin_port = htons(peerPort);

    int ret = sendto(socketfd,
                     packet.data(),
                     packet.size(),
                     0,
                     (struct sockaddr *)&peerAddr,
                     sizeof(peerAddr));

    if (ret < 0)
    {
        perror("Fail to sendto");
        return false;
    }

    return true;
}

/**
 * 接收函数
 */
bool UdpNetCommunication::recvPacket(vector<uint8_t> &packet, int timeout)
{
    fd_set readfds;

    struct timeval waiteTime = {.tv_sec = timeout};
    struct timeval *ptimeout = timeout < 0 ? nullptr : &waiteTime;

    FD_ZERO(&readfds);
    FD_SET(socketfd, &readfds);

    int ret = select(socketfd + 1, &readfds, NULL, NULL, ptimeout);
    if (ret < 0)
    {
        perror("Fail to select \n");
        return false;
    }
    else if (ret == 0)
    {
        fprintf(stderr, "Select timeout \n");
        return false;
    }
    uint8_t buf[1500];
    struct sockaddr_in peerAddr;
    socklen_t addrlen = sizeof(struct sockaddr);
    int size = recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&peerAddr, &addrlen);
    if (size < 0)
    {
        perror("Fail to recvFrom");
        return false;
    }
    packet = vector<uint8_t>(buf, buf + size);

    // 获取对方的ip和端口
    peerIP = inet_ntoa(peerAddr.sin_addr);
    peerPort = ntohs(peerAddr.sin_port);

    return true;
}
